package com.tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.enums.CommentType;
import com.tanhua.dubbo.server.enums.IdType;
import com.tanhua.dubbo.server.pojo.*;
import com.tanhua.dubbo.server.service.IdService;
import com.tanhua.dubbo.server.service.TimeLineService;
import com.tanhua.dubbo.server.vo.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;

/**
 * 查询好友动态（时间线表）
 */
@Service(version = "1.0.0")
@Slf4j
public class QuanZiApiImpl implements QuanZiApi{

    //评论数据存储在Redis中key的前缀
    private static final String COMMENT_REDIS_KEY_PREFIX = "QUANZI_COMMENT_";

    //用户是否点赞的前缀
    private static final String COMMENT_USER_LIEK_REDIS_KEY_PREFIX = "USER_LIKE_";

    //用户是否喜欢的前缀
    private static final String COMMENT_USER_LOVE_REDIS_KEY_PREFIX = "USER_LOVE_";

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private IdService idService;

    @Autowired
    private TimeLineService timeLineService;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private VideoApi videoApi;

    /**
     * 查询好友动态
     *
     * @param userId 当前用户id
     * @param pageNum 当前页数
     * @param pageSize 每一页条数
     * @return
     */
    @Override
    public PageInfo<Publish> queryPublishList(Long userId, Integer pageNum, Integer pageSize) {
        //构建一个结果对象
        PageInfo<Publish> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(pageNum);
        pageInfo.setPageSize(pageSize);

        //分页
        Pageable pageable = PageRequest.of(pageNum - 1,pageSize, Sort.by(Sort.Order.desc("date")));
        //MongoDB查询条件
        Query query = new Query().with(pageable);
        //查询结果集合
        List<TimeLine> timeLineList = mongoTemplate.find(query, TimeLine.class, "quanzi_time_line_" + userId);

        //查询结果如果为null
        if (CollUtil.isEmpty(timeLineList)){
            //直接返回结果对象
            return pageInfo;
        }

        //拿取时间表所查询集合的所有发布id
        List<Object> idList = CollUtil.getFieldValues(timeLineList,"publishId");

        //根据发布id到发布表中去查询动态的完整信息
        Query queryPublish = Query.query(Criteria.where("id").in(idList)).with(Sort.by(Sort.Order.desc("created")));
        List<Publish> publishList = mongoTemplate.find(queryPublish, Publish.class);

        //将好友动态的完整信息的集合封装到结果对象中，返回给调用者
        pageInfo.setRecords(publishList);
        return pageInfo;
    }

    /**
     * 发布动态
     * @param publish
     * @return
     */
    @Override
    public String savePublish(Publish publish) {
        //判断动态文本和发布用户id是否为null
        if(!ObjectUtil.isAllNotEmpty(publish.getText(),publish.getUserId())){
            return null;
        }

        //设置主键ID  ??不懂这个ObjectId类
        publish.setId(ObjectId.get());

        try {
            //设置一个自增长的PId
            publish.setPid(idService.createId(IdType.PUBLISH));
            //设置创建动态时间
            long timeMillis = System.currentTimeMillis();
            publish.setCreated(timeMillis);

            //存入发布表
            mongoTemplate.save(publish);

            //新建一个相册对象
            Album album = new Album();
            //发布id
            album.setPublishId(publish.getId());
            //创建时间
            album.setCreated(timeMillis);
            //存入相册表
            mongoTemplate.save(album, "quanzi_album_" + publish.getUserId());

            //异步写入好友时间表
            timeLineService.saveTimeLine(publish.getId(), publish.getUserId(),timeMillis);
        }catch (Exception e){
            log.error("发布动态失败",e);
            return null;
        }

        return publish.getId().toHexString();
    }

    /**
     * 推荐好友
     *
     * @param userId 用户id
     * @param page 当前页数
     * @param pageSize 每一页查询的数据条数
     * @return
     */
    @Override
    public PageInfo<Publish> queryRecommendPublishList(Long userId, Integer page, Integer pageSize) {

        //构建一个结果对象
        PageInfo<Publish> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);

        //查询redis中关于当前用户的推荐用户pid信息
        String redisKey = "QUANZI_PUBLISH_RECOMMEND_" + userId;
        String data = redisTemplate.opsForValue().get(redisKey);
        //是否有对应信息
        if (StrUtil.isEmpty(data)){
            return pageInfo;
        }

        //获得推荐用户的pid
        String[] pids = StringUtils.split(data, ",");

        //计算分页 ？？？
        int[] ints = PageUtil.transToStartEnd(page - 1, pageSize);
        int start = ints[0];//开始
        int end = Math.min(ints[1],pids.length);//结束

        //把所有的pid取出来
        List<Long> pidLongList = new ArrayList<>();
        for (int i = start; i < end; i++) {
            pidLongList.add(Long.valueOf(pids[i]));
        }
        //集合没有数据
        if (CollUtil.isEmpty(pidLongList)){
            return pageInfo;
        }

        //根据pid查询发布表中的具体动态
        Query query = Query.query(Criteria.where("pid").in(pidLongList)).with(Sort.by(Sort.Order.desc("created")));
        List<Publish> publishes = mongoTemplate.find(query, Publish.class);
        if (CollUtil.isEmpty(publishes)){
            //没有查询出数据
            return pageInfo;
        }

        //查询出数据，将集合封装到结果对象中
        pageInfo.setRecords(publishes);
        return pageInfo;
    }

    //-------------------------------------------点赞-------------------------------------------------

    /**
     * 按id查询动态
     * 根据动态id到MongoDB中查询出这条动态的具体信息
     * @param id 动态id
     * @return Publish对象
     */
    @Override
    public Publish queryPublishById(String id) {
        return mongoTemplate.findById(new ObjectId(id),Publish.class);
    }

    /**
     * 点赞
     * 1.判断是否点赞过这个动态
     * 2.点赞的用户id和被点赞的动态id及其余信息封装到Comment对象中，保存到MongoDB中
     * 3.点赞的用户id和被点赞的动态id组成redisKey和hashKey，将点赞的用户信息和点赞的条数保存到redis中
     * @param userId 用户id
     * @param publishId 动态id
     * @return 是否点赞成功
     */
    @Override
    public Boolean likeComment(Long userId, String publishId) {
        //1. 判断是否已经点赞过这个动态
        Boolean bool = queryUserIsLike(userId, publishId);
        if (bool){
            //点赞过了返回false
            return false;
        }

        //2. 保存到MongoDB
       /* //创建评论表实体类对象
        Comment comment = new Comment();
        //主键id
        comment.setId(ObjectId.get());
        //动态id
        comment.setPublishId(new ObjectId(publishId));
        //评论类型
        comment.setCommentType(CommentType.LIKE.getType());
        //评论内容
        comment.setContent(null);
        //评论人的id
        comment.setUserId(userId);
        //发布这条动态的用户的id
        comment.setPublishUserId(queryPublishById(publishId).getUserId());
        //这条评论的创建时间
        comment.setCreated(System.currentTimeMillis());
        //保存到MongoDB
        mongoTemplate.save(comment);*/
        Boolean save = saveComment(userId, publishId, CommentType.LIKE, null);

        if (!save) {
            return false;
        }

        //3. 缓存到redis中
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = COMMENT_USER_LIEK_REDIS_KEY_PREFIX + userId;
        //添加用户点赞信息
        redisTemplate.opsForHash().put(redisKey,hashKey,"1");
        //点赞数自增
        redisTemplate.opsForHash().increment(redisKey,"LIKE",1);

        return true;
    }

    /**
     * 取消点赞
     * @param userId 用户id
     * @param publishId 动态id
     * @return
     */
    @Override
    public Boolean disLikeComment(Long userId, String publishId) {
        //查询是否点赞
        Boolean bool = queryUserIsLike(userId, publishId);
        if (!bool){
                //还没有点赞，不能取消点赞
                return false;
        }

        //1. 删除redis中的数据
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = COMMENT_USER_LIEK_REDIS_KEY_PREFIX + userId;
        redisTemplate.opsForHash().delete(redisKey,hashKey);

        //修改redis中的点赞数
        redisTemplate.opsForHash().increment(redisKey,"LIKE",-1);

        //2.删除MongoDB中的数据
        Query query = Query.query(Criteria.where("userId").is(userId)
                .and("publishId").is(new ObjectId(publishId))
                .and("commentType").is(CommentType.LIKE.getType())
        );
        long count = mongoTemplate.remove(query, Comment.class).getDeletedCount();
        return true;
    }

    /**
     * 查询点赞数
     * 1.从redis中直接查询hashkey中对应字段的value，有值就直接返回
     * 2.redis中没有查询到，到MongoDB中查询，然后写入redis中，返回点赞数
     * @param publishId 动态id
     * @return 点赞数量
     */
    @Override
    public Long queryLikeCount(String publishId) {
        //1.redis中查询点赞数
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        Object data = redisTemplate.opsForHash().get(redisKey, CommentType.LIKE.toString());
        if (ObjectUtil.isNotEmpty(data)){
            return Convert.toLong(data);
        }
        //2.查询MongoDB
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(CommentType.LIKE.getType())
        );
        long count = mongoTemplate.count(query, Comment.class);
        //写入redis中
        redisTemplate.opsForHash().put(redisKey,"LIKE",String.valueOf(count));
        return count;
    }

    /**
     * 查询用户是否点赞该动态
     * @param userId 用户id
     * @param publishId 动态id
     * @return
     */
    @Override
    public Boolean queryUserIsLike(Long userId, String publishId) {
        //1.redis中查询
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = COMMENT_USER_LIEK_REDIS_KEY_PREFIX + userId;
        Object data = redisTemplate.opsForHash().get(redisKey, hashKey);
        if (ObjectUtil.isNotEmpty(data)){
            //点赞
            return true;
        }

        //2. 查询MongoDB
        Query query = Query.query(Criteria.where("publishId").is(publishId).and("userId").is(userId).and("commentType").is(CommentType.LIKE.getType()));
        long count = mongoTemplate.count(query, Comment.class);
        if (count == 0){
            return false;
        }

        //3. 缓存到redis中
        redisTemplate.opsForHash().put(redisKey,hashKey,"1");

        return true;
    }

    /**
     * 查询数量
     *
     * @param publishId
     * @param commentType
     * @return
     */
    private Long queryCommentCount(String publishId, CommentType commentType) {
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType.getType())
        );
        return this.mongoTemplate.count(query, Comment.class);
    }

    //-------------------------------------------喜欢-------------------------------------------------

    /**
     * 喜欢
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean loveComment(Long userId, String publishId) {
        //1. 判断是否已经喜欢过这个动态
        Boolean bool = queryUserIsLove(userId, publishId);
        if (bool){
            //点赞过了返回false
            return false;
        }

        //2. 保存到MongoDB
        /*//创建评论表实体类对象
        Comment comment = new Comment();
        //主键id
        comment.setId(ObjectId.get());
        //动态id
        comment.setPublishId(new ObjectId(publishId));
        //评论类型
        comment.setCommentType(CommentType.LOVE.getType());
        //评论内容
        comment.setContent(null);
        //评论人的id
        comment.setUserId(userId);
        //发布这条动态的用户的id
        comment.setPublishUserId(queryPublishById(publishId).getUserId());
        //这条评论的创建时间
        comment.setCreated(System.currentTimeMillis());
        //保存到MongoDB
        mongoTemplate.save(comment);*/
        Boolean saveComment = saveComment(userId, publishId, CommentType.LOVE, null);

        if (!saveComment){
            return false;
        }

        //3. 缓存到redis中
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
        //添加用户点赞信息
        redisTemplate.opsForHash().put(redisKey,hashKey,"1");
        //点赞数自增
        redisTemplate.opsForHash().increment(redisKey,"LOVE",1);

        return true;
    }

    /**
     * 取消喜欢
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean disLoveComment(Long userId, String publishId) {
        //查询是否点赞
        Boolean bool = queryUserIsLove(userId, publishId);
        if (!bool){
            //还没有点赞，不能取消点赞
            return false;
        }

        //1. 删除redis中的数据
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
        redisTemplate.opsForHash().delete(redisKey,hashKey);

        //修改redis中的点赞数
        redisTemplate.opsForHash().increment(redisKey,"LOVE",-1);

        //2.删除MongoDB中的数据
        Query query = Query.query(Criteria.where("userId").is(userId)
                .and("publishId").is(new ObjectId(publishId))
                .and("commentType").is(CommentType.LOVE.getType())
        );
        long count = mongoTemplate.remove(query, Comment.class).getDeletedCount();
        return true;
    }

    /**
     * 查询喜欢数
     * @param publishId
     * @return
     */
    @Override
    public Long queryLoveCount(String publishId) {
        //1.redis中查询点赞数
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        Object data = redisTemplate.opsForHash().get(redisKey, CommentType.LOVE.toString());
        if (ObjectUtil.isNotEmpty(data)){
            return Convert.toLong(data);
        }
        //2.查询MongoDB
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(CommentType.LOVE.getType())
        );
        long count = mongoTemplate.count(query, Comment.class);
        //写入redis中
        redisTemplate.opsForHash().put(redisKey,"LOVE",String.valueOf(count));
        return count;
    }

    /**
     * 查询这个用户有没有喜欢
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean queryUserIsLove(Long userId, String publishId) {
        //1.redis中查询
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
        Object data = redisTemplate.opsForHash().get(redisKey, hashKey);
        if (ObjectUtil.isNotEmpty(data)){
            //点赞
            return true;
        }

        //2. 查询MongoDB
        Query query = Query.query(Criteria.where("publishId").is(publishId).and("userId").is(userId).and("commentType").is(CommentType.LOVE.getType()));
        long count = mongoTemplate.count(query, Comment.class);
        if (count == 0){
            return false;
        }

        //3. 缓存到redis中
        redisTemplate.opsForHash().put(redisKey,hashKey,"1");

        return true;
    }

    //-------------------------------------------评论-------------------------------------------------

    /**
     * 查询评论列表
     * @param publishId 评论的发布id
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryCommentList(String publishId, Integer page, Integer pageSize) {
        PageInfo<Comment> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);

        //分页、排序
        Pageable pageable = PageRequest.of(page-1,pageSize,Sort.by(Sort.Order.asc("created")));
        //查询这条动态(publishId)的评论(commentType)进行分页
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
        .and("commentType").is(CommentType.COMMENT.getType())).with(pageable);
        List<Comment> commentList = mongoTemplate.find(query, Comment.class);
        //将查询的的评论列表封装到结果对象中
        pageInfo.setRecords(commentList);
        return pageInfo;
    }

    /**
     * 保存Comment
     *
     * @return
     */
    private Boolean saveComment(Long userId, String publishId,
                                CommentType commentType, String content) {
        try {
            Comment comment = new Comment();
            comment.setId(ObjectId.get());
            comment.setUserId(userId);
            comment.setPublishId(new ObjectId(publishId));
            // 评论类型
            comment.setCommentType(commentType.getType());
            // 内容
            comment.setContent(content);
            comment.setCreated(System.currentTimeMillis());

//            if (commentType.getType() == 1){
//                Publish publish = this.queryPublishById(publishId);
//                comment.setPublishUserId(publish.getUserId());
//            }

            Publish publish = this.queryPublishById(publishId);
            if (ObjectUtil.isNotEmpty(publish)) {
                comment.setPublishUserId(publish.getUserId());
            } else {
                //查询评论
                Comment myComment = this.queryCommentById(publishId);
                if(ObjectUtil.isNotEmpty(myComment)){
                    comment.setPublishUserId(myComment.getUserId());
                }else{
                    //查询小视频
                    Video video = this.videoApi.queryVideoById(publishId);
                    if(ObjectUtil.isNotEmpty(video)){
                        comment.setPublishUserId(video.getUserId());
                    }else{
                        // 其他情况，直接返回
                        return false;
                    }
                }
            }

            this.mongoTemplate.save(comment);

            return true;
        } catch (Exception e) {
            log.error("保存Comment出错~ userId = " + userId + ", publishId = " + publishId + ", commentType = " + commentType, e);
        }

        return false;
    }

    /**
     * 根据id查询Comment对象
     *
     * @param id
     * @return
     */
    private Comment queryCommentById(String id) {
        return this.mongoTemplate.findById(new ObjectId(id), Comment.class);
    }

    /**
     * 发布评论
     * @param userId
     * @param publishId
     * @param content
     * @return
     */
    @Override
    public Boolean saveComment(Long userId, String publishId, String content) {
        //调用saveComment方法，直接将评论数据存到MongoDB中
        Boolean bool = saveComment(userId, publishId,CommentType.COMMENT, content);
        return bool;
    }

    /**
     * 查询评论数
     * @param publishId
     * @return
     */
    @Override
    public Long queryCommentCount(String publishId) {
        return queryCommentCount(publishId, CommentType.COMMENT);
    }

    //----------------------------------------消息----------------------------------------------------

    /**
     * 查询点赞我的列表
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryLikeCommentListByUser(Long userId, Integer page, Integer pageSize) {
//        PageInfo<Comment> pageInfo = new PageInfo<>();
//        pageInfo.setPageNum(page);
//        pageInfo.setPageSize(pageSize);
//
//        //根基用户id查询评论表中LIKE类型的数据，分页，按时间倒序排序
//        PageRequest pageRequest = PageRequest.of(page-1,pageSize,Sort.by(Sort.Order.desc("created")));
//        Query query = Query.query(Criteria.where("publishUserId").is(userId).and("commentType").is(CommentType.LIKE.getType())).with(pageRequest);
//        List<Comment> commentList = mongoTemplate.find(query, Comment.class);
//        if (CollUtil.isEmpty(commentList)){
//            return pageInfo;
//        }
//        pageInfo.setRecords(commentList);
//        return pageInfo;
        return queryCommentListByUser(userId,CommentType.LIKE,page,pageSize);
    }

    /**
     * 查询喜欢我的列表
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryLoveCommentListByUser(Long userId, Integer page, Integer pageSize) {
        return queryCommentListByUser(userId,CommentType.LOVE,page,pageSize);
    }

    /**
     * 查询评论我的列表
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryCommentListByUser(Long userId, Integer page, Integer pageSize) {
        return queryCommentListByUser(userId,CommentType.COMMENT,page,pageSize);
    }

    /**
     * 查询评论类型的列表
     * @param userId
     * @param commentType
     * @param page
     * @param pageSize
     * @return
     */
    private PageInfo<Comment> queryCommentListByUser(Long userId, CommentType commentType, Integer page, Integer pageSize) {
        PageInfo<Comment> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);

        //根基用户id查询评论表中LIKE类型的数据，分页，按时间倒序排序
        PageRequest pageRequest = PageRequest.of(page-1,pageSize,Sort.by(Sort.Order.desc("created")));
        Query query = Query.query(Criteria.where("publishUserId").is(userId).and("commentType").is(commentType.getType())).with(pageRequest);
        List<Comment> commentList = mongoTemplate.find(query, Comment.class);
        if (CollUtil.isEmpty(commentList)){
            return pageInfo;
        }
        pageInfo.setRecords(commentList);
        return pageInfo;
    }

    /**
     * 查询个人相册（查询相册表然后查询发布表）
     * @param userId 个人相册所属用户的id
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Publish> queryAlbumList(Long userId, Integer page, Integer pageSize) {
        PageInfo<Publish> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);

        //查询该用户id相册表的所有数据
        PageRequest pageRequest = PageRequest.of(page-1,pageSize,Sort.by(Sort.Order.desc("created")));
        Query AlbumQuery = new Query().with(pageRequest);
        List<Album> albumList = mongoTemplate.find(AlbumQuery,Album.class,"quanzi_album_"+userId);
        if (CollUtil.isEmpty(albumList)){
            return pageInfo;
        }

        //获得发布id到发布表查询具体内容
        List<Object> publishId = CollUtil.getFieldValues(albumList, "publishId");
        Query query = Query.query(Criteria.where("id").in(publishId)).with(pageRequest);
        List<Publish> publishList = mongoTemplate.find(query, Publish.class);
        if (CollUtil.isEmpty(publishList)){
            return pageInfo;
        }

        pageInfo.setRecords(publishList);
        return pageInfo;
    }
}

